home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d8 / qsnip2.arc / QSNIP.ASM < prev    next >
Assembly Source File  |  1991-04-28  |  27KB  |  792 lines

  1. page 60,132
  2. ; QSNIP is a resident utility which allows cutting out a portion
  3. ; of the screen.  The selected portion may be printed, written
  4. ; to disk or entered in the keyboard buffer.  Activate QSNIP by
  5. ; pressing ALT-TAB, then position the cursor in the upper left corner of
  6. ; the window using the arrow keys.  Press CR to fix the first corner,
  7. ; then expand the window with arrow keys.  Finally, type "S" to Save,
  8. ; "P" to print, "F" for disk file, "G" to retrieve or CR for a help menu.
  9. ; Press ESC any time to exit QSNIP.  When installing QSNIP, use the optional
  10. ; parameters to expand it's internal buffer for displays (such as the
  11. ; EGA) containing more than the standard 25 rows and 80 columns.
  12. ;   QSNIP  [rows,columns]
  13.  
  14. ;------------------------------------;
  15. ; BIOS_SEG IS THE ROM-BIOS DATA AREA ;
  16. ;------------------------------------;
  17. BIOS_SEG    SEGMENT    AT 0040H
  18.         ORG    004AH
  19. CRT_COLS    DB    ?        ;CURRENT NUMBER OF COLUMNS
  20.         ORG    0050H
  21. CURSOR_POSN    DW    8 DUP(?)    ;CURRENT CURSOR LOCATION
  22.         ORG    0062H
  23. ACTIVE_PAGE    DB    ?        ;ACTIVE PAGE FOR CGA AND EGA
  24.         ORG    0084H
  25. ROWS        DB    ?        ;LAST ROW NUMBER FOR EGA
  26. BIOS_SEG    ENDS
  27.  
  28. CSEG        SEGMENT
  29.         ASSUME    CS:CSEG,DS:NOTHING
  30.         ORG    0100H        ;BEGINNING FOR .COM PROGRAMS
  31. START:        JMP    INITIALIZE    ;INITIALIZATION CODE IS AT END
  32.  
  33. ;--------------------------------;
  34. ; DATA AREA USED BY THIS PROGRAM ;
  35. ;--------------------------------;
  36. HOTKEY        EQU    0FH        ;SCAN CODE FOR TAB KEY
  37. SHIFT_MASK    EQU    00001000B    ;MASK FOR ALT KEY
  38. ;
  39. COPYRIGHT    DB    "QSNIP - Based on Snipper 1.0 (c) 1987 Ziff Communications Co."
  40.         DB    13,10,"Hotkey is ALT-TAB"
  41.                 DB      13,10,"Modified for Quoting Functions within Qmodem"
  42.                 DB      13,10,"By Ken Burt",13,10,"$",1AH
  43. PROGRAMMER    DB    "Tom Kihlken-Modified by Ken Burt"
  44. INSTALLED_MSG    DB    "Already Installed",13,10,"$"
  45. BAD_DOS_MSG    DB    "Requires DOS 2.0+",13,10,"$"
  46. OLDINT09    DD    ?    ;OLD KEYBOARD BREAK INTERRUPT VECTOR
  47. OLDINT13    DD    ?    ;OLD BIOS DISK IO INTERRUPT VECTOR
  48. OLDINT16    DD    ?    ;OLD KEYBOARD INTERRUPT VECTOR
  49. OLDINT21    DD    ?    ;OLD DOS FUNCTION INTERRUPT VECTOR
  50. ERR_STAT    DB    ?    ;ERROR STATUS DURING FILE OUTPUT
  51. FILE_PROMPT    DB    "Enter Filename: "
  52. FILENAME    DB    "SCREEN.CUT"    ;THE DEFAULT FILENAME
  53.         DB    15 DUP (0)    ;LEAVE ROOM FOR DRIVE AND PATH
  54. BUFF_NEXT    DW      BUFF_START    ;POINTER TO NEXT KEY IN BUFFER
  55. BUFF_LAST    DW      BUFF_START    ;POINTER TO LAST KEY IN BUFFER
  56. BUFF_START    EQU    OFFSET INITIALIZE
  57. BUFF_SIZE    EQU    25*(80+2)    ;ROOM FOR 25 ROWS OF 80 COLUMNS
  58. BUFF_END    DW    BUFF_START+BUFF_SIZE
  59. TOP_LEFT    LABEL    WORD        ;FIRST CORNER OF WINDOW
  60. LEFT_SIDE    DB    0        ;COLUMN NUMBER OF LEFT SIDE
  61. TOP_ROW        DB    0        ;ROW NUMBER OF TOP SIDE
  62. BOT_RIGHT    LABEL    WORD        ;SECOND CORNER OF WINDOW
  63. RIGHT_SIDE    DB    ?        ;COLUMN NUMBER OR RIGHT SIDE
  64. BOT_ROW        DB    ?        ;ROW NUMBER OF BOTTOM
  65. SEND_CHAR    DW    ?        ;POINTER TO CHARACTER HANDLER
  66. SEND_KEYS    DB    0        ;IF=1, USE KEYSTROKES FROM BUFFER
  67. WRIT_FILE    DB    0        ;IF=1, NEED TO WRITE TO DISK
  68. BUSY_FLAGS    DB    0        ;BIT MASKED AS FOLLOWS:
  69.                     ; 1 - DOS IS ACTIVE
  70.                     ; 2 - BIOS IO IS ACTIVE
  71.                     ; 4 - SNIPPER IS ACTIVE
  72. DOS_STAT    DB    0        ;CURRENT DOS FUNCTION
  73.  
  74. HELP_MENU    DB    201,10 DUP(205),187
  75.         DB    186," F - File ",186
  76.         DB    186," P - Print",186
  77.         DB    186," S - Save ",186
  78.         DB    186," G - Get  ",186
  79.         DB    186,"Esc- Quit ",186
  80.         DB    200,10 DUP(205),188
  81.  
  82. ;------------------------------------------------------------------;
  83. ; SNIPPER BUILDS THE WINDOW AND ACCEPTS COMMANDS FROM THE KEYBOARD ;
  84. ;------------------------------------------------------------------;
  85. SNIPPER        PROC    NEAR
  86.         ASSUME    DS:CSEG, ES:BIOS_SEG
  87.         XOR    BX,BX        ;BX IS INCREMENT FOR ROW/COLUMN
  88. GET_KB_KEY1:
  89.         MOV    DX,TOP_LEFT    ;GET LOCATION OF FIRST CORNER
  90.         ADD    DH,BH        ;ADD IN THE ROW INCREMENT
  91.         ADD    DL,BL        ;ADD IN THE COLUMN INCREMENT
  92.         CMP    DL,0        ;AT LEFT EDGE OF SCREEN?
  93.         JGE    NOT_LEFT_EDGE
  94.         MOV    DL,CRT_COLS    ;JUMP TO THE RIGHT EDGE
  95.         DEC    DL
  96. NOT_LEFT_EDGE:
  97.         CMP    DL,CRT_COLS    ;AT RIGHT EDGE OF SCREEN YET?
  98.         JB    NOT_RIGHT_EDGE    ;IF NOT, KEEP MOVING RIGHT
  99.         XOR    DL,DL        ;IF YES, WRAP TO LEFT EDGE
  100. NOT_RIGHT_EDGE:
  101.         CMP    DH,0        ;AT TOP OF SCREEN YET?
  102.         JGE    NOT_AT_TOP
  103.         MOV    DH,ROWS        ;JUMP DOWN TO THE BOTTOM
  104. NOT_AT_TOP:
  105.         CMP    DH,ROWS        ;AT BOTTOM OF SCREEN?
  106.         JLE    NOT_AT_BOTTOM
  107.         XOR    DH,DH        ;JUMP BACK TO THE TOP
  108. NOT_AT_BOTTOM:
  109.         MOV    TOP_LEFT,DX    ;SAVE NEW CORNER LOCATION
  110.         CALL    REV_VIDEO    ;CHANGE IT TO REVERSE VIDEO
  111.         XOR    AH,AH        ;BIOS KEYBOARD INPUT
  112.         INT    16H        ;GET A KEYSTROKE
  113.         PUSH    AX
  114.         CALL    REV_VIDEO    ;PUT ATTRIBUTE BACK TO NORMAL
  115.         POP    AX
  116.         CMP    AH,1        ;IS IT ESCAPE?
  117.         JNE    NOT_ESC
  118.         RET            ;JUST RETURN TO EXIT
  119. NOT_ESC:
  120.         MOV    BX,0FF00H    ;INCREMENT TO SUBTRACT ONE ROW
  121.         CMP    AH,48H        ;IS IT UP ARROW?
  122.         JE    GET_KB_KEY1
  123.         MOV    BX,0100H    ;INCREMENT TO ADD ONE ROW
  124.         CMP    AH,50H        ;IS IT DOWN ARROW?
  125.         JE    GET_KB_KEY1
  126.         MOV    BX,0001H    ;INCREMENT TO ADD ONE COLUMN
  127.         CMP    AH,4DH        ;IS IT RIGHT ARROW?
  128.         JE    GET_KB_KEY1
  129.         MOV    BX,00FFH    ;INCREMENT TO SUBTRACT ONE COLUMN
  130.         CMP    AH,4BH        ;IS IT LEFT ARROW?
  131.         JE    GET_KB_KEY1
  132.         XOR    BX,BX
  133.         CMP    AL,13        ;IS IT A CARRIAGE RETURN?
  134.         JNE    NOT_CR
  135.         MOV    DX,TOP_LEFT    ;A CARRIAGE RETURN WAS PRESSED
  136.         MOV    BOT_RIGHT,DX    ;INITIALIZE THE SECOND CORNER
  137.         CALL    REV_VIDEO    ;CHANGE IT BACK TO REVERSE VIDEO
  138.         JMP    SHORT GET_KB_KEY2
  139. NOT_CR:
  140.         CMP    AH,22H        ;IS IT THE "G" KEY
  141.         JE    TYPE_BUFF    ;IF YES, THAN GET THE WINDOW
  142.         JMP    GET_KB_KEY1    ;JUST GET ANOTHER KEY
  143. TYPE_BUFF:
  144.         MOV    SEND_KEYS,1    ;SIGNAL TO SEND THE KEYS
  145.         RET
  146. GET_KB_KEY2:
  147.         XOR    AH,AH
  148.         INT    16H        ;GET A KEYSTROKE
  149. GOT_KEY2:    MOV    DX,BOT_RIGHT
  150.         CMP    AH,48H        ;IS IT UP ARROW?
  151.         JE    SUB_ROW        ;SUBTRACT A ROW FROM WINDOW
  152.         CMP    AH,50H        ;IS IT DOWN ARROW?
  153.         JE    ADD_ROW        ;ADD A ROW TO THE WINDOW
  154.         CMP    AH,4DH        ;IS IT RIGHT ARROW?
  155.         JE    ADD_COL        ;ADD A COLUMN TO THE WINDOW
  156.         CMP    AH,4BH        ;IS IT LEFT ARROW?
  157.         JE    SUB_COL        ;SUBTRACT A COLUMN FROM WINDOW
  158.         JMP    NOT_ARROW_KEY
  159. SUB_COL:
  160.         DEC    DL        ;SUBTRACT A COLUMN
  161.         CMP    DL,LEFT_SIDE    ;DONT ERASE IT COMPLETELY
  162.         JL    GET_KB_KEY2
  163.         MOV    RIGHT_SIDE,DL    ;SAVE NEW RIGHT SIDE COLUMN
  164.         INC    DL
  165.         JMP    SHORT COL_LOOP
  166. ADD_COL:
  167.         INC    DL        ;ADD A COLUMN
  168.         CMP    DL,CRT_COLS    ;AT RIGHT EDGE OF SCREEN?
  169.         JAE    GET_KB_KEY2    ;STOP WHEN SCREEN IS FILLED
  170.         MOV    RIGHT_SIDE,DL    ;SAVE NEW RIGHT SIDE COLUMN
  171. COL_LOOP:
  172.         CALL    REV_VIDEO    ;REVERSE THIS CHARACTER
  173.         DEC    DH        ;MOVE TO NEXT ROW
  174.         CMP    DH,TOP_ROW    ;AT TOP ROW YET?
  175.         JGE    COL_LOOP    ;LOOP UNTIL AT TOP ROW
  176.         JMP    GET_KB_KEY2
  177. SUB_ROW:
  178.         DEC    DH
  179.         CMP    DH,TOP_ROW    ;AT TOP OF WINDOW?
  180.         JL    GET_KB_KEY2    ;DONT ERASE IT COMPLETELY
  181.         MOV    BOT_ROW,DH
  182.         INC    DH
  183.         JMP    SHORT ROW_LOOP
  184. ADD_ROW:
  185.         INC    DH
  186.         CMP    DH,ROWS        ;AT BOTTOM OF SCREEN?
  187.         JG    GET_KB_KEY2    ;STOP WHEN SCREEN IS FILLED
  188.         MOV    BOT_ROW,DH
  189. ROW_LOOP:
  190.         CALL    REV_VIDEO    ;REVERSE THIS CHARACTER
  191.         DEC    DL        ;MOVE TO NEXT COLUMN
  192.         CMP    DL,LEFT_SIDE    ;AT LEFT EDGE YET?
  193.         JGE    ROW_LOOP    ;CONTINUE UNTIL AT LEFT EDGE
  194.         JMP    GET_KB_KEY2
  195. NOT_ARROW_KEY:
  196.         CMP    AH,19H        ;WAS IT THE "P" KEY?
  197.         JNE    NOT_P
  198.         MOV    SEND_CHAR,OFFSET PRINT_CHAR
  199.         JMP    READ_WINDOW
  200. NOT_P:
  201.         MOV    BUFF_NEXT,BUFF_START
  202.         MOV    BUFF_LAST,BUFF_START
  203.         MOV    SEND_CHAR,OFFSET BUFF_CHAR
  204.  
  205.         CMP    AH,1FH        ;WAS IT THE "S" KEY?
  206.         JNE    NOT_S
  207.         MOV    SEND_CHAR,OFFSET BUFF_CHAR
  208.         JMP    READ_WINDOW
  209. NOT_S:
  210.         CMP    AH,22H        ;IS IT THE "G" KEY
  211.         JNE    NOT_G
  212.         MOV    SEND_KEYS,1
  213.         JMP    READ_WINDOW
  214. NOT_G:
  215.         CMP    AH,21H        ;IS IT THE "F" KEY
  216.         JNE    NOT_F
  217.         MOV    WRIT_FILE,0
  218.         CALL    GET_FILENAME
  219.         CMP    WRIT_FILE,-1    ;WAS ESCAPE REQUESTED?
  220.         JE    ERASE_BOX
  221.         CALL    READ_WINDOW
  222.         MOV    WRIT_FILE,1
  223.         TEST    BUSY_FLAGS,00000011B     ;IS INT21 OR INT13 BUSY?
  224.         JNZ    RETURN        ;IF YES, WAIT TILL LATER
  225.         CALL    WRITE_TO_FILE    ;IF NOT, DO IT NOW
  226. RETURN:
  227.         RET
  228. NOT_F:
  229.         CMP    AH,1        ;IS IT ESCAPE?
  230.         JE    ERASE_BOX ;IF YES, ERASE BOX AND EXIT
  231.         CMP    AL,13        ;IS IT A CARRIAGE RETURN?
  232.         JE    DISPLAY_HELP    ;IF YES, DISPLAY HELP
  233.         JMP    GET_KB_KEY2    ;OTHERWISE JUST GET ANOTHER KEY
  234. ERASE_BOX:
  235.         MOV    SEND_CHAR,OFFSET RETURN
  236.         JMP    READ_WINDOW
  237. DISPLAY_HELP:
  238.         CALL    EXCHANGE_HELP    ;PUT UP THE HELP MENU
  239.         XOR    AH,AH
  240.         INT    16H        ;GET ANOTHER KEYSTROKE
  241.         PUSH    AX        ;SAVE THE KEYSTROKE
  242.         CALL    EXCHANGE_HELP    ;PULL DOWN THE HELP MENU
  243.         POP    AX        ;GET BACK THE KEYSTROKE
  244.         JMP    GOT_KEY2
  245. ;*********************************************************************
  246. REV_VIDEO:
  247.         CALL    READ_CHAR    ;READ CHARACTER AND ATTRIBUTE
  248.         MOV    BL,AH        ;SAVE ATTRIBUTE IN BL
  249.         AND    BL,10001000B    ;GET BLINK AND INTENSITY BITS
  250.         AND    AH,01110111B    ;NOW LOOK ONLY AT COLOR BITS
  251.         MOV    CL,4        ;ROTATE FOUR COUNTS
  252.         ROR    AH,CL        ;ROTATE FOREGROUND AND BACKGROUND
  253.         OR    BL,AH        ;PUT BACK BLINK AND INTENSITY BITS
  254.         CALL    DISPLAY_CHAR    ;WRITE CHARACTER AND ATTRIBUTE
  255.         RET
  256. ;*********************************************************************
  257. READ_WINDOW:
  258.         MOV    DX,TOP_LEFT    ;GET LOCATION OF FIRST CORNER
  259.                 MOV     AL,62
  260.                 CALL    SEND_CHAR
  261.                 MOV     AL,32
  262.                 CALL    SEND_CHAR
  263. READ_LOOP:
  264.         CALL    REV_VIDEO    ;PUT ATTRIBUTE BACK TO NORMAL
  265.         CALL    READ_CHAR    ;READ THE CHARACTER
  266.         CALL    SEND_CHAR    ;CALL TO THE POINTER
  267.         INC    DL        ;NEXT CHAR IN ROW
  268.         CMP    DL,RIGHT_SIDE    ;AT THE RIGHT BORDER YET?
  269.         JLE    READ_LOOP    ;DO ALL CHARACTERS IN THIS ROW
  270.                 CALL    CR_LF        ;SEND CR-LF AFTER EACH ROW
  271.         INC    DH        ;MOVE TO NEXT ROW
  272.         MOV    DL,LEFT_SIDE    ;BACK TO LEFT EDGE
  273.         CMP    DH,BOT_ROW    ;AT THE BOTTOM BORDER YET?
  274.         JLE    READ_LOOP    ;READ ENTIRE WINDOW
  275.                 RET
  276. ;*********************************************************************
  277. CR_LF:
  278.         MOV    AL,13
  279.         CALL    SEND_CHAR    ;SEND A CARRIAGE RETURN
  280.         MOV    AL,10
  281.         CALL    SEND_CHAR    ;SEND A LINE FEED
  282.         CMP     DH,BOT_ROW      ;Is it the last line?
  283.         JE      GET_OUT         ;If so, don't send the >
  284.         MOV     AL,62           
  285.         CALL    SEND_CHAR       ;SEND A >
  286.         MOV     AL,32
  287.         CALL    SEND_CHAR       ;SEND A SPACE
  288. GET_OUT:
  289.          RET
  290. ;*********************************************************************
  291. DISPLAY_CHAR:
  292.         PUSH    BX        ;SAVE THE ATTRIBUTE
  293.         CALL    GET_CURS_ADDR    ;GET ADDRESS OF BIOS CURSOR
  294.         MOV    ES:[BX],DX    ;TELL BIOS WHERE THE CURSOR IS
  295.         POP    BX        ;GET BACK THE ATTRIBUTE
  296.         MOV    BH,ACTIVE_PAGE    ;GET ACTIVE PAGE
  297.         PUSH    CX        ;SAVE THE LOOP COUNT
  298.         MOV    CX,1        ;WRITE 1 CHARACTER
  299.         MOV    AH,9        ;WRITE CHARACTER AND ATTRIBUTE
  300.         INT    10H
  301.         POP    CX        ;RECOVER LOOP COUNT
  302.         RET            ;DONE WRITING THE CHARACTER
  303. ;*********************************************************************
  304. READ_CHAR:
  305.         CALL    GET_CURS_ADDR    ;GET ADDRESS OF BIOS CURSOR
  306.         MOV    ES:[BX],DX    ;TELL BIOS WHERE THE CURSOR IS
  307.         MOV    BH,ACTIVE_PAGE    ;GET ACTIVE PAGE
  308.         MOV    AH,8        ;BIOS FUNCTION TO READ CHARACTER
  309.         INT    10H        ;READ THE CHARACTER/ATTRIBUTE
  310.         RET
  311. ;*********************************************************************
  312. PRINT_CHAR:
  313.         PUSH    DX
  314.         XOR    AH,AH        ;USE FUNCTION 0
  315.         XOR    DX,DX        ;PRINTER NUMBER 0
  316.         INT    17H        ;BIOS PRINT CHARACTER FUNCTION
  317.         ROR    AH,1        ;LOOK AT BIT ZERO
  318.         JNC    PRINT_OK    ;DID A TIMEOUT OCCUR?
  319.         MOV    SEND_CHAR,OFFSET RETURN
  320. PRINT_OK:
  321.         POP    DX
  322.         RET            ;DONE PRINTING CHARACTER
  323. ;*********************************************************************
  324. BUFF_CHAR:
  325.         MOV    BX,BUFF_LAST    ;GET LOCATION OF LAST CHARACTER
  326.         MOV    [BX],AL        ;PUT THE CHARACTER IN BUFFER
  327.         INC    BX        ;ADVANCE THE POINTER
  328.         MOV    BUFF_LAST,BX    ;CHECK FOR BUFFER FULL
  329.         CMP    BX,BUFF_END    ;IS THE BUFFER FULL YET?
  330.         JNE    BUFF_OK        ;IF NOT, KEEP GOING
  331.         MOV    SEND_CHAR,OFFSET RETURN
  332. BUFF_OK:
  333.         RET            ;NOW ITS IN THE BUFFER
  334. ;*********************************************************************
  335. GET_CURS_ADDR:
  336.         MOV    BL,ACTIVE_PAGE    ;GET THE CURRENT PAGE NUMBER
  337.         XOR    BH,BH        ;CONVERT TO A WORD OFFSET
  338.         SHL    BX,1        ;TIMES TWO FOR A WORD
  339.         ADD    BX,OFFSET CURSOR_POSN ;ADD IN BASE ADDRESS
  340.         RET
  341. ;*********************************************************************
  342. EXCHANGE_HELP:
  343.         XOR    DX,DX        ;START AT TOP LEFT CORNER
  344.         LEA    SI,HELP_MENU
  345. EXCHANGE_LOOP:
  346.         CMP    DL,12        ;AT LAST COLUMN IN THIS ROW YET?
  347.         JL    SWAP_CHAR
  348.         XOR    DL,DL        ;BACK TO FIRST COLUMN
  349.         INC    DH        ;DO THE NEXT ROW
  350.         CMP    DH,7        ;AT LAST ROW YET?
  351.         JL    SWAP_CHAR    ;QUIT WHEN LAST ROW IS DONE
  352.         RET
  353. SWAP_CHAR:
  354.         CALL    READ_CHAR    ;READ CHARACTER AT THIS POSITION
  355.         XCHG    AL,CS:[SI]    ;SWAP WITH THE HELP TEXT
  356.         MOV    BL,AH        ;ATTRIBUTE IS THE SAME
  357.         CALL    DISPLAY_CHAR    ;PUT NEW CHARACTER ON SCREEN
  358.         INC    DL        ;POINT TO NEXT POSITION
  359.         INC    SI
  360.         JMP EXCHANGE_LOOP
  361. ;*********************************************************************
  362. GET_FILENAME:
  363.         LEA    SI,FILE_PROMPT    ;POINT TO THE PROMPT FOR SOURCE
  364.         XOR    DI,DI        ;USE THE PSP FOR BUFFER
  365.         XOR    DX,DX        ;PUT PROMPT AT TOP LEFT CORNER
  366.         MOV    CX,40        ;USE MAX OF 40 CHARACTERS
  367. DISPLAY_PROMPT:
  368.         PUSH    CX        ;SAVE LOOP COUNT
  369.         CALL    READ_CHAR    ;GET CHARACTER ON THIS LINE
  370.         MOV    CS:[DI],AX    ;STORE IT IN THE PSP
  371.         INC    DI        ;ADD TWO FOR NEXT CHARACTER
  372.         INC    DI
  373.         MOV    AL,CS:[SI]    ;GET NEXT PROMPT CHARACTER
  374.         INC    SI        ;NEXT CHARACTER IN PROMPT
  375.         MOV    BL,47H        ;ATTRIBUTE FOR PROMPT
  376.         CALL    DISPLAY_CHAR    ;PUT UP THE PROMPT CHARACTER
  377.         INC    DL        ;POINT TO NEXT COLUMN
  378.         POP    CX        ;GET BACK LOOP COUNT
  379.         LOOP    DISPLAY_PROMPT    ;ENTIRE PROMPT AND FILENAME
  380. FIND_LAST_LETTER:
  381.         DEC    SI        ;BACKUP TO LAST LETTER
  382.         DEC    DL        ;BACKUP TO LAST COLUMN
  383.         CMP    BYTE PTR [SI],0    ;IS THIS A LETTER?
  384.         JE    FIND_LAST_LETTER;BACKUP UNTILL A LETTER IS FOUND
  385.         INC    DL        ;PUT BLINKING BOX AT LAST LETTER
  386. READ_KB:
  387.         MOV    AL,219        ;ASCII FOR BOX CHARACTER
  388.         MOV    BL,47H+80H    ;MAKE IT A BLINKING BOX CHARACTER
  389.         CALL    DISPLAY_CHAR    ;WRITE THE BLINKING BOX
  390. ;
  391.         XOR    AH,AH        ;FUNCTIO 0 TO GET NEXT KEY
  392.         INT    16H        ;BIOS KEYBOARD INPUT
  393.         CMP    AL,13        ;IS IT A CARRIAGE RETURN?
  394.         JE    ERASE_PROMPT
  395.         CMP    AL,8        ;IS IT A BACKSPACE?
  396.         JE    BACK_SPACE
  397.         CMP    AH,1        ;IS IT ESCAPE?
  398.         JE    ESC_RET
  399.         CMP    AL,"."        ;IS IT A VALID LETTER?
  400.         JL    READ_KB
  401.         CMP    AL,"z"        ;IS IT A VALID LETTER?
  402.         JG    READ_KB
  403.         CMP    DL,39        ;ONLY ALLOW 40 CHARACTERS
  404.         JGE    READ_KB
  405. TTY_KEY:
  406.         MOV    BL,47H        ;ATTRIBUTE FOR FILENAME
  407.         CALL    DISPLAY_CHAR    ;WRITE THE LETTER
  408.         INC    DL        ;MOVE TO NEXT COLUMN
  409.         JMP    READ_KB        ;GET ANOTHER KEYSTROKE
  410. BACK_SPACE:
  411.         CMP    DL,16        ;AT BEGINNING OF LINE?
  412.         JLE    READ_KB        ;IF YES, CAN'T BACKUP FROM HERE
  413.         MOV    AL,0        ;WRITE A NORMAL BLANK (ASCII 0)
  414.         MOV    BL,47H        ;ATTRIBUTE FOR FILENAME
  415.         CALL    DISPLAY_CHAR    ;WRITE THE LETTER
  416.         DEC    DL        ;BACKUP THE CURSOR
  417.         JMP    READ_KB        ;THEN GET THE NEXT KEY
  418. ESC_RET:
  419.         MOV    WRIT_FILE,-1    ;INDICATE ESCAPE IS REQUESTED
  420. ERASE_PROMPT:
  421.         XOR    AL,AL        ;GET RID OF THE CURSOR
  422.         CALL    DISPLAY_CHAR    ;WRITE THE LETTER
  423.         LEA    DI,FILE_PROMPT    ;COPY TO FILENAME
  424.         XOR    SI,SI        ;COPY FROM PSP
  425.         XOR    DX,DX        ;PROMPT IS AT ROW ZERO
  426.         MOV    CX,40        ;COPY ALL 40 CHARACTERS
  427. ERASE_LOOP:
  428.         CALL    READ_CHAR    ;GET CHARACTER ON THIS LINE
  429.         MOV    CS:[DI],AL    ;PUT IN BACK IN MEMORY
  430.         INC    DI
  431.         MOV    AX,CS:[SI]    ;GET THE ORIGINAL CHARACTER BACK
  432.         MOV    BL,AH        ;PUT ATTRIBUTE INTO BL
  433.         INC    SI
  434.         INC    SI
  435.         CALL    DISPLAY_CHAR    ;WRITE ORIGINAL CHARACTER
  436.         INC    DL        ;MOVE TO NEXT COLUMN
  437.         LOOP    ERASE_LOOP    ;ERASE THE ENTIRE PROMPT
  438.         RET
  439. SNIPPER        ENDP
  440.  
  441. ;---------------------------------------------------------------------;
  442. ; THIS COPIES THE BUFFER CONTENTS TO A FILE. IT SHOULD ONLY BE CALLED ;
  443. ; WHEN DOS IS IN A STABLE AND REENTRANT CONDITION.                    ;
  444. ;---------------------------------------------------------------------;
  445. WRITE_TO_FILE    PROC    NEAR
  446.         ASSUME    DS:NOTHING, ES:NOTHING
  447.  
  448.         MOV    WRIT_FILE,0    ;TURN OFF REQUEST FLAG
  449.         PUSH    AX        ;MUST PRESERVE ALL REGISTERS
  450.         PUSH    BX
  451.         PUSH    CX
  452.         PUSH    DX
  453.         PUSH    DS
  454.         PUSH    ES
  455.         PUSH    CS
  456.         POP    DS
  457.         ASSUME    DS:CSEG        ;DS POINTS TO OUR CODE SEGMENT
  458.              MOV    AX,3524H    ;GET DOS CRITICAL ERROR VECTOR
  459.         INT    21H        ;DOS FUNCTION TO GET VECTOR
  460.         PUSH    BX        ;SAVE OLD VECTOR ON STACK
  461.         PUSH    ES
  462.  
  463. ; REPLACE THE DOS SEVERE ERROR INTERRUPT WITH OUR OWN ROUTINE.
  464.  
  465.         MOV    DX,OFFSET NEWINT24
  466.         MOV    AX,2524H    ;SETUP TO CHANGE INT 24h VECTOR
  467.         INT    21H        ;CHANGE DOS SEVERE ERROR VECTOR
  468.         MOV    DX,OFFSET FILENAME ;POINT TO FILENAME
  469.  
  470. ; FIRST TRY TO OPEN THE FILE.  IF DOS RETURNS WITH THE CARRY FLAG SET,
  471. ; THE FILE DIDN'T EXIST AND WE MUST CREATE IT.  ONCE THE FILE IS OPENED,
  472. ; ADVANCE THE FILE POINTER TO THE END OF FILE TO APPEND.
  473.  
  474.         MOV    AX,3D02H    ;DOS FUNCTION TO OPEN FILE
  475.         INT    21H        ;DOS WILL RETURN WITH CARRY FLAG
  476.         JC    FILE_NOT_FOUND    ;SET IF FILE DOESN'T EXIST.
  477.         MOV    BX,AX        ;KEEP HANDLE IN BX ALSO
  478.         XOR    CX,CX        ;MOVE DOS FILE POINTER TO THE
  479.         XOR    DX,DX        ;END OF THE FILE. THIS LETS US
  480.         MOV    AX,4202H    ;APPEND THIS TO AN EXISTING FILE
  481.         INT    21H        ;DOS FUNCTION TO MOVE POINTER
  482.         JNC    WRITE_FILE    ;IF NO ERROR, CONTINUE TO WRITE
  483. DOS_ERROR:
  484.         CMP    ERR_STAT,0    ;DID A SEVERE ERROR OCCUR?
  485.         JNE    REP_VECTOR    ;IF SEVERE ERROR, JUST QUIT
  486.         JMP    SHORT CLOSE_FILE;JUST CLOSE THE FILE
  487.  
  488. FILE_NOT_FOUND:    CMP    ERR_STAT,0    ;DID A SEVERE ERROR OCCUR?
  489.         JNE    REP_VECTOR    ;IF SEVERE ERROR, JUST QUIT
  490.         MOV    CX,0020H    ;ATTRIBUTE FOR NEW FILE
  491.         MOV    AH,3CH        ;CREATE FILE FOR WRITING
  492.         INT    21H        ;DOS FUNCTION TO CREATE FILE
  493.         JC    DOS_ERROR       ;ON ANY ERROR, TAKE JUMP
  494.         MOV    BX,AX        ;SAVE HANDLE IN BX
  495. WRITE_FILE:     MOV    DX,BUFF_START    ;POINT TO BUFFER
  496.         MOV    CX,BUFF_LAST    ;GET BUFFER POINTER
  497.         SUB    CX,DX        ;NUMBER OF CHARS IN BUFFER
  498.         MOV    AH,40H        ;DOS WRITE TO A DEVICE FUNCTION
  499.         INT    21H        ;WRITE TO THE FILE
  500. CLOSE_FILE:
  501.         MOV    AH,3EH        ;DOS FUNCTION TO CLOSE THE FILE
  502.         INT    21H
  503. REP_VECTOR:
  504.         POP    DS        ;GET INT 24H VECTOR FROM STACK
  505.         POP    DX
  506.         MOV    AX,2524H    ;RESTORE CRITICAL ERROR VECTOR
  507.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  508.         POP    ES        ;FINALLY RESTORE ALL REGISTERS
  509.         POP    DS
  510.         POP    DX
  511.         POP    CX
  512.         POP    BX
  513.         POP    AX
  514.         RET            ;FINISHED WRITING TO DISK
  515. WRITE_TO_FILE    ENDP
  516. ;---------------------------------------------------------------------;
  517. ; INTERRUPT 09 ROUTINE.  WATCH FOR TRIGGER KEY TO POP UP.
  518. ;---------------------------------------------------------------------;
  519. NEWINT09    PROC    FAR
  520.         ASSUME    DS:NOTHING, ES:NOTHING
  521.         STI            ;ALLOW OTHER INTERRUPTS
  522.         PUSH    AX        ;MUST SAVE PROCESSOR STATE
  523.         IN    AL,60H        ;GET THE SCAN CODE
  524.         CMP    AL,HOTKEY    ;IS IT THE HOT KEY?
  525.         JE    TRIGGER        ;IF YES, CHECK THE MASK
  526. INT09_EXIT:    POP    AX        ;RESTORE THE PROCESSOR STATE
  527.         JMP    OLDINT09    ;CONTINUE WITH ROM ROUTINE
  528.  
  529. TRIGGER:    MOV    AH,2        ;GET KEYBOARD STATUS
  530.         INT    16H        ;BIOS KEYBOARD SERVICE
  531.  
  532.         AND    AL,0FH        ;Take lo er four bits
  533.         CMP    AL,SHIFT_MASK    ;IS ALT KEY DOWN?
  534.         JNZ    INT09_EXIT    ;IF NOT, IGNORE IT
  535.         TEST    BUSY_FLAGS,00000100B ;IS SNIPPER ALREADY ACTIVE?
  536.         JNZ    INT09_EXIT    ;IF ACTIVE, THEN EXIT
  537.         OR    BUSY_FLAGS,00000100B ;ITS ACTIVE NOW
  538.         PUSHF
  539.         CALL    OLDINT09    ;LET ROM PROCESS THE KEY
  540.         PUSH    BX        ;MUST PRESERVE ALL REGISTERS
  541.         PUSH    CX
  542.         PUSH    DX
  543.         PUSH    BP
  544.         PUSH    SI
  545.         PUSH    DI
  546.         PUSH    DS
  547.         PUSH    ES
  548.         PUSH    CS
  549.         POP    DS        ;SET DS TO CSEG
  550.         MOV    AX,BIOS_SEG    ;ES POINTS TO BIOS DATA AREA
  551.         MOV    ES,AX
  552.         ASSUME    DS:CSEG, ES:BIOS_SEG
  553.         CALL    GET_CURS_ADDR    ;CURSOR ADDRESS FOR THIS PAGE
  554.         PUSH    ES:[BX]        ;SAVE THE CURSOR LOCATION
  555.         CALL    SNIPPER        ;DO THE WINDOW
  556.         CALL    GET_CURS_ADDR    ;CURS0R ADDRESS FOR THIS PAGE
  557.         POP    ES:[BX]        ;GET BACK CURSOR  POSITION
  558.         AND    BUSY_FLAGS,11111011B  ;SNIPPER IS NOT ACTIVE
  559.         POP    ES        ;RESTORE ALL REGISTERS
  560.         POP    DS
  561.         POP    DI
  562.         POP    SI
  563.         POP    BP
  564.         POP    DX
  565.         POP    CX
  566.         POP    BX
  567.         POP    AX
  568.         IRET            ;NOW WERE ALL DONE
  569. NEWINT09    ENDP
  570. ;---------------------------------------------------------------------;
  571. ; INTERRUPT 13 ROUTINE. SET BIOS BUST BIT                             ;
  572. ;---------------------------------------------------------------------;
  573. NEWINT13    PROC    FAR
  574.         ASSUME    DS:NOTHING, ES:NOTHING
  575.         OR    BUSY_FLAGS,00000010B    ;SET BIOS BUSY BIT
  576.         PUSHF
  577.         CALL    OLDINT13    ;DO THE BIOS FUNCTION
  578.         PUSHF            ;SAVE RESULT FLAGS
  579.         AND    BUSY_FLAGS,11111101B    ;CLEAR BIOS BUSY BIT
  580.         POPF            ;GET BACK RESULT FLAGS
  581.         STI            ;MUST RETURN WITH INTERUPTS ON
  582.         RET    2        ;RETURN BIOS RESULT FLAGS
  583. NEWINT13    ENDP
  584. ;---------------------------------------------------------------------;
  585. ; INTERRUPT 16 ROUTINE. INSERT KEYSTROKES FROM BUFFER                 ;
  586. ;---------------------------------------------------------------------;
  587. NEWINT16    PROC    FAR
  588.         ASSUME    DS:NOTHING, ES:NOTHING
  589.         PUSH    BX
  590.         CMP    SEND_KEYS,1    ;SENDING KEYS FROM BUFFER?
  591.         JE    INSERT_KEY    ;IF YES, THEN GET NEXT ONE
  592.         CMP    WRIT_FILE,1    ;ANYTHING TO WRITE TO DISK?
  593.         JE    CHECK_DOS_STAT    ;IF YES, THIS IS THE TIME
  594. BIOS_KB:
  595.         POP    BX
  596.         JMP    OLDINT16    ;JUST DO NORMAL KB ROUTINE
  597. CHECK_DOS_STAT:
  598.         CMP    DOS_STAT,0AH    ;DOING READ STRING?
  599.         JE    BEGIN_NOW    ;IF YES, ITS SAFE TO BEGIN
  600.         CMP    DOS_STAT,8    ;DOING KEYBOARD INPUT?
  601.         JNE    BIOS_KB        ;IF YES, ITS SAFE TO BEGIN
  602. BEGIN_NOW:
  603.         STI            ;GET INTERRUPTS BACK ON
  604.         CALL    WRITE_TO_FILE    ;EMPTY THE BUFFER
  605.         JMP    BIOS_KB        ;CONTINUE WITH BIOS ROUTINE
  606. INSERT_KEY:
  607.         STI            ;INTERRUPTS BACK ON
  608.         MOV    BX,BUFF_NEXT    ;GET ADDRESS OF NEXT BYTE
  609.         CMP    BX,BUFF_LAST    ;AT END OF BUFFER YET?
  610.         JL    GET_A_KEY    ;IF NOT, GET THE NEXT ONE
  611.         MOV    SEND_KEYS,0    ;WHEN DONE, TURN OFF SEND SWITCH
  612. GET_A_KEY:
  613.         MOV    AL,CS:[BX]    ;GET THE NEXT KEY CODE
  614.         CMP    AL,10        ;IS IT A LINE FEED?
  615.         JNE    NOT_LF        ;DONT RETURN THE LINE FEEDS
  616.         INC    BUFF_NEXT    ;SKIP TO NEXT KEY
  617.         JMP    INSERT_KEY
  618. NOT_LF:
  619.         CMP    AH,1        ;REQUEST FOR STATUS ONLY?
  620.         JE    RETURN_STATUS    ;IF YES, RETURN STATUS ONLY
  621.         CMP    AH,0        ;REQUEST TO GET THE NEXT KEY
  622.         JNE    BIOS_KB        ;IF NOT, IGNORE THIS FUNCTION
  623.         INC    BX        ;REMOVE THIS KEY FROM OUR BUFFER
  624.         MOV    BUFF_NEXT,BX    ;SAVE THE POINTER TO NEXT KEY
  625. RETURN_STATUS:
  626.         OR    BL,1        ;CLEAR ZERO FLAG TO INDICATE A
  627.         POP    BX        ;KEY IS AVAILIABLE
  628.         RET    2        ;RETURN WITH THESE FLAGS
  629. NEWINT16    ENDP
  630. ;---------------------------------------------------------------------;
  631. ; INTERRUPT 21 ROUTINE.  THIS ROUTINE IS USED TO MONITOR DOS FUNCTION ;
  632. ; CALLS. IF THE BUFFER NEEDS TO BE FLUSHED, IT WIL BE DONE HERE.      ;
  633. ;---------------------------------------------------------------------;
  634. NEWINT21    PROC    FAR
  635.         ASSUME    DS:NOTHING, ES:NOTHING
  636.         STI
  637.         OR    AH,AH        ;DOING FUNCTION ZERO?
  638.         JNE    NOT_ZERO
  639.         MOV    AH,4CH        ;IF YES, CHANGE IT TO A 4CH
  640. NOT_ZERO:
  641.         OR    BUSY_FLAGS,00000001B    ;SET DOS BUSY BIT
  642.         MOV    DOS_STAT,AH
  643.         PUSHF            ;SIMULATE AN INTERRUPT
  644.         CALL    OLDINT21    ;DO THE DOS FUNCTION
  645.         PUSHF            ;SAVE THE RESULT FLAGS
  646.         AND    BUSY_FLAGS,11111110B    ;CLEAR DOS BUSY BIT
  647.         CMP    WRIT_FILE,1    ;ANYTHING TO WRITE TO DISK?
  648.         JNE    NO_WRITE    ;IF NOT JUST RETURN
  649.         CALL    WRITE_TO_FILE    ;SAFE TO ACCESS DISK NOW
  650. NO_WRITE:
  651.         POPF            ;RECOVER DOS RESULT FLAGS
  652.         RET    2        ;RETURN WITH DOS RESULT FLAGS
  653. NEWINT21    ENDP
  654.  
  655. ;---------------------------------------------------------------------;
  656. ; NEW INTERRUPT 24H (CRITICAL DOS ERROR).  THIS INTERRUPT IS ONLY IN  ;
  657. ; EFFECT ONLY DURING A WRITE SCREEN.  IT IS REQUIRED TO SUPPRESS THE  ;
  658. ; 'ABORT, RETRY, IGNORE' MESSAGE.  ALL FATAL DISK ERRORS ARE IGNORED. ;
  659. ;---------------------------------------------------------------------;
  660. NEWINT24    PROC    FAR
  661.         ASSUME    CS:CSEG, DS:NOTHING, ES:NOTHING
  662.         STI            ;TURN INTERRUPTS BACK ON
  663.         INC     ERR_STAT    ;SET THE ERROR FLAG
  664.          XOR    AL,AL        ;TELLS DOS TO IGNORE THE ERROR
  665.         IRET            ;THATS ALL WE DO HERE
  666. NEWINT24    ENDP
  667.  
  668. ;--------------------------------------------------------------------;
  669. ; HERE IS THE CODE USED TO INITIALIZE SNIPPER.                       ;
  670. ;--------------------------------------------------------------------;
  671.         ASSUME    CS:CSEG, DS:CSEG, ES:CSEG
  672. INITIALIZE:
  673.         LEA    DX,COPYRIGHT
  674.         MOV    AH,9        ;DOS DISPLAY STRING SERVICE
  675.         INT    21H        ;DISPLAY TITLE MESSAGE
  676. ; SEARCH FOR AN PREVIOUSLY INSTALLED COPY OF SNIPPER
  677.         NOT    BYTE PTR START    ;MODIFY TO AVOID FASLE MATCH
  678.         XOR    BX,BX        ;START SEARCH AT SEGMENT ZERO
  679.         MOV    AX,CS        ;COMPARE TO THIS CODE SEGMENT
  680. NEXT_SEGMENT:
  681.         INC    BX        ;LOOK AT NEXT SEGMENT
  682.         CMP    AX,BX        ;UNTIL REACHING THIS CODE SEG
  683.         MOV    ES,BX
  684.         JE    NOT_INSTALLED
  685.         LEA    SI,START    ;SETUP TO COMPARE STRINGS
  686.         MOV    DI,SI
  687.         MOV    CX,16        ;16 BYTES MUST MATCH
  688.         REP    CMPSB        ;COMPARE DS:SI TO ES:DI
  689.         OR    CX,CX        ;DID THE STRINGS MATCH?
  690.         JNZ    NEXT_SEGMENT    ;IF NO MATCH, TRY NEXT SEGMENT
  691.         LEA    DX,INSTALLED_MSG
  692.         JMP    SHORT ERR_EXIT
  693. NOT_INSTALLED:
  694.         MOV    AH,30H
  695.         INT    21H        ;GET DOS VERSION NUMBER
  696.         CMP    AL,2        ;IS IT HIGHER THAN 2.0?
  697.         JAE    VER_OK        ;IF YES, PROCEED
  698.         LEA    DX,BAD_DOS_MSG
  699. ERR_EXIT:    MOV    AH,9        ;DOS DISPLAY STRING SERVICE
  700.         INT    21H        ;DISPLAY ERRER MESSAGE
  701.         RET            ;RETURN TO DOS
  702. VER_OK:
  703.         INC    SI        ;POINT TO FIRST PARAMETER
  704.         MOV    SI,81H        ;POINT TO PARAMETER AREA
  705.         CALL    GET_PARAM    ;GET FIRST PARAMETER (ROWS)
  706.         PUSH    AX        ;SAVE THE ROW COUNT
  707.         CALL    GET_PARAM    ;GET SECOND PARAMETER (COLUMNS)
  708.         ADD    AX,2        ;ADD SPACE FOR CR AND LF
  709.         POP    BX        ;GET BACK FIRST PARAMETER
  710.         MUL    BX        ;PRODUCT OF ROWS AND COLUMNS
  711.         OR    AX,AX        ;WAS ANYTHING ENTERED?
  712.         JZ    NO_PARAMS    ;IF NOT, USE DEFAULT VALUE
  713.         CMP    AX,10000    ;MAXIMUM BUFFER IS 10000 BYTES
  714.         JLE    SIZE_IS_OK
  715.         MOV    AX,10000
  716. SIZE_IS_OK:
  717.         ADD    AX,BUFF_START
  718.         MOV    BUFF_END,AX    ;SET THE NEW BUFFER SIZE
  719. NO_PARAMS:
  720.         MOV    AX,BIOS_SEG    ;LOOK AT BIOS DATA AREA
  721.         MOV    ES,AX
  722.         ASSUME    ES:BIOS_SEG
  723.         CMP    ROWS,0        ;IS NUMBER OF ROWS ENTERED HERE
  724.         JNE    MUST_BE_EGA    ;IF YES, AN EGA MAY BE PRESENT
  725.         MOV    ROWS,24        ;IF NOT EGA, MUST BE 24 ROWS
  726. MUST_BE_EGA:
  727.         ASSUME    ES:NOTHING
  728.                MOV    AX,3509H    ;GET KEYBOARD BREAK VECTOR
  729.         INT    21H
  730.         MOV    WORD PTR [OLDINT09],  BX  ;SAVE SEGMENT
  731.         MOV    WORD PTR [OLDINT09+2],ES  ;SAVE OFFSET
  732.         MOV    DX, OFFSET NEWINT09
  733.         MOV    AX, 2509H
  734.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  735.  
  736.                MOV    AX,3513H    ;GET BIOS DISK INTERRUPT VECTOR
  737.         INT    21H
  738.         MOV    WORD PTR [OLDINT13],  BX  ;SAVE SEGMENT
  739.         MOV    WORD PTR [OLDINT13+2],ES  ;SAVE OFFSET
  740.         MOV    DX, OFFSET NEWINT13
  741.         MOV    AX, 2513H
  742.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  743.  
  744.                MOV    AX,3516H    ;GET KEYBOARD INPUT VECTOR
  745.         INT    21H
  746.         MOV    WORD PTR [OLDINT16],  BX  ;SAVE SEGMENT
  747.         MOV    WORD PTR [OLDINT16+2],ES  ;SAVE OFFSET
  748.         MOV    DX, OFFSET NEWINT16
  749.         MOV    AX, 2516H
  750.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  751.  
  752.                MOV    AX,3521H    ;GET DOS FUNCTION VECTOR
  753.         INT    21H
  754.         MOV    WORD PTR [OLDINT21],  BX
  755.         MOV    WORD PTR [OLDINT21+2],ES
  756.         MOV    DX, OFFSET NEWINT21
  757.         MOV    AX, 2521H
  758.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  759.  
  760. ;--------------------------------------------------------------------;
  761. ; DEALLOCATE OUR COPY OF THE ENVIORNMENT.                            ;
  762. ; EXIT USING INT 27H. LEAVE CODE AND SPACE FOR BUFFER RESIDENT.      ;
  763. ;--------------------------------------------------------------------;
  764.  
  765.         MOV    AX,DS:[002CH]    ;GET SEGMENT OF ENVIORNMENT
  766.         MOV    ES,AX        ;PUT IT INTO ES
  767.         MOV    AH,49H        ;RELEASE ALLOCATED MEMORY
  768.         INT    21H
  769.         MOV    DX,BUFF_END    ;LEAVE THIS MUCH RESIDENT
  770.         INT    27H        ;TEMINATE AND STAY RESIDENT
  771. ;---------------------------------------------------------;
  772. ; GET_PARAM RETRIEVES AN INTEGER FROM THE COMMAND LINE.   ;
  773. ;---------------------------------------------------------;
  774. GET_PARAM:    XOR    AX,AX        ;CLEAR AX FOR TOTAL
  775. GET_DIGIT:    MOV    BL,[SI]        ;GET CHARACTER INTO BL
  776.         CMP    BL,0DH        ;IS IT THE LAST ONE?
  777.         JE    DONE
  778.         INC    SI        ;POINT TO NEXT CHARACTER
  779.         CMP    BL,","        ;IS IT THE DELIMITER?
  780.         JE    DONE
  781.         SUB    BL,30H        ;CONVERT ASCII TO INTEGER
  782.         JC    GET_DIGIT    ;IS IT A VALID DIGIT
  783.         CMP    BL,9
  784.         JA    GET_DIGIT    ;IF NOT VALID, JUST SKIP IT
  785.         MOV    BH,10        ;TIMES 10 FOR NEXT DIGIT
  786.         MUL    BH        ;MULTIPLY SUM AND ADD THIS DIGIT
  787.         ADD    AL,BL        ;ADD DIGIT TO SUM
  788.         JMP    GET_DIGIT    ;READ ALL CHARACTERS ON LINE
  789. DONE:        RET
  790. CSEG        ENDS
  791.         END    START
  792.